﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace X86Instructions
{
    public enum X86InstPrefix:byte
    {
        NONE = 0,
        //group 1
        LOCK=0xf0,
        REPNE=0xf2,
        REP =0xf3,
        //group 2
        CS = 0x2e,
        SS = 0x36,
        DS = 0x3e,
        ES = 0x26,
        FS = 0x64,
        GS = 0x65,

        BranchNotTaken = 0x2e,
        BranchTaken = 0x3e,
        //group 3
        OperandSizeOverride = 0x66,
        //group 4
        AddressSizeOverride = 0x67,

    }


    public enum MainInstructionGroup:byte
    {
        Prefix,
        General,
        System,
        X87FPU,
    }

    public enum GeneralInstructionSubGroup:byte
    {
        DataMov,
        Stack,
        Convertion,
        Arith,
        Logical,
        Shftrot,
        Bit,
        Branch,
        Break,
        String,
        Inout,
        FlagCtl,
        Segreg,
        Control,
    }

    public class X86InstructionInfo
    {
        public Byte Prefix;
        public Byte MultiByteFlag;
        public Byte PrimaryOpcode;
        public Byte SecondaryOpcode;
        public Byte OpcodeFields;
        public Byte UseModRM;
        public Byte OpcodeExtension;
        public RingLevel Ring;
        public Byte LockPrefixAllowed;
        public X86InstMnemonic Mnemonic;

        public InstructionExtensionGroup iExt;

        public MainInstructionGroup MainGroup;
        public Byte SubGroup;

        public X86FlagBit TestFlag;
        public X86FlagBit ModifiedFlag;
        public String Description;

        /// <summary>
        /// Initialize a one byte generl instruction
        /// </summary>
        /// <param name="PrimaryOpcode"></param>
        /// <param name="Mnemonic"></param>
        /// <param name="GenGroup"></param>
        /// <param name="FlagTest"></param>
        /// <param name="FlagModified"></param>
        /// <param name="Description"></param>
        public X86InstructionInfo(Byte PrimaryOpcode, X86InstMnemonic Mnemonic, GeneralInstructionSubGroup GenGroup, X86FlagBit FlagTest, X86FlagBit FlagModified, String Description)
            :this(0, 0, PrimaryOpcode, 0, 0, 1, 0, RingLevel.All, 0, Mnemonic, InstructionExtensionGroup.General,
                MainInstructionGroup.General, (byte)GenGroup, FlagTest, FlagModified, Description)
        {

        }

        public X86InstructionInfo(Byte Pfx, Byte MultiByte, Byte PriOpcode, Byte SecOpcode, Byte OpcFields, Byte ModRM, Byte OpcExt,
            RingLevel ringLevel, Byte Lock, X86InstMnemonic mnemonic, InstructionExtensionGroup ext, MainInstructionGroup GrpMain,
            Byte GrpSub, X86FlagBit FlagTest, X86FlagBit FlagModified, String Desp)
        {
            this.Prefix = Pfx;
            this.MultiByteFlag = MultiByte;
            this.PrimaryOpcode = PriOpcode;
            this.SecondaryOpcode = SecOpcode;
            this.OpcodeFields = OpcFields;
            this.UseModRM = ModRM;
            this.OpcodeExtension = OpcExt;
            this.Ring = ringLevel;
            this.LockPrefixAllowed = Lock;
            this.Mnemonic = mnemonic;
            this.iExt = ext;
            this.MainGroup = GrpMain;
            this.SubGroup = GrpSub;
            this.TestFlag = FlagTest;
            this.ModifiedFlag = FlagModified;
            this.Description = Desp;
        }
    }

    

    public class X86PrefixBytes
    {
        public List<Byte> Bytes = new List<byte>(1);

        public X86PrefixBytes(Byte pfx)
        {
            Bytes.Add(pfx);
        }

        public void Add(Byte pfx)
        {
            Bytes.Add(pfx);
        }

        public X86InstPrefix this[int index]
        {
            get {
                if (index >= Bytes.Count)
                    throw new IndexOutOfRangeException();

                return (X86InstPrefix)Enum.ToObject(typeof(X86InstPrefix), Bytes[index]);
            }
        }

        public int Length
        {
            get { return Bytes.Count; }
        }
    }

    public enum X86OpcodeDirection
    {
        REGToModRM,
        ModRmToREG,

        NotSupported,
    }

    public enum X86OperandSize
    {
        _8bit,
        _16bit,
        _32bit,
        _64bit,
    }

    public enum _8BitRegister
    {
        AL,
        BL,
        CL,
        DL,
        AH,
        BH,
        CH,
        DH,
        None,
        Unknown,
    }
 
    public enum _16BitRegister
    {
        AX,
        BX,
        CX,
        DX,
        SP,
        BP,
        SI,
        DI,
        None,
        Unknown,
    }

    public enum _32BitRegister
    {
        EAX,
        EBX,
        ECX,
        EDX,
        ESP,
        EBP,
        ESI,
        EDI,

        None,
        Unknown,
    }

    //MMX instruction set
    //64 bit registers
    public enum MMRegister
    {
        MM0,
        MM1,
        MM2,
        MM3,
        MM4,
        MM5,
        MM6,
        MM7,
    }

    //SSE instruction set
    //128bit registers
    public enum XMMRegister
    {
        MM0,
        MM1,
        MM2,
        MM3,
        MM4,
        MM5,
        MM6,
        MM7,
    }

    public enum InstructionExtensionGroup
    {
        General,
        System,
        X87FPU,
        MMX,
        SSE,
        VMX,
        SMX
    }

    public enum RingLevel
    {
        All,
        _0,
        _1,
        _2,
        _3,
    }

    [Flags]
    public enum X86FlagBit
    {
        OF = 0x400,
        DF = 0x200,
        IF = 0x100,
        TF = 0x80,
        SF = 0x40,
        ZF = 0x20,
        Undef3 = 0x10,
        AF = 0x8,
        Undef2 = 0x4,
        PF = 0x2,
        Undef1 = 1,
        CF = 0,
    }

    public struct X86OpcodeBytes
    {
        public UInt16 Bytes;
        public X86InstMnemonic Mnemonic
        {
            get { 
                X86Dism.Initialize();
                if ((Bytes >> 8) == 0x0f) //multi byte opcode
                {
                    X86InstructionInfo info = X86Dism.DoubleByteInstructionTable[(Byte)Bytes];
                    if (info != null)
                        return info.Mnemonic;
                    else
                        return X86InstMnemonic.Unknown;
                }
                else
                    return X86Dism.SingleByteInstructionTable[(Byte)Bytes].Mnemonic;
            }
        }
        
        public Byte DirectionSignExtBit { get { return (Byte)((Bytes & 0x2) >> 1); } } //bit before last one
        public Byte SizeBit { get { return (Byte)(Bytes & 0x1); } } //last bit
        public Byte ImmediateBit { get { return (Byte)((Bytes & 0x80) >> 7); } } //high bit;

        //Attributions according to values

        public X86OpcodeDirection Direction
        {
            get {
                System.Diagnostics.Debug.Assert(ImmediateBit != 1, "Cannot get direction with immediate bit");
                 
                return (DirectionSignExtBit == 0) ? X86OpcodeDirection.REGToModRM : X86OpcodeDirection.ModRmToREG;
            }
        }

        public Boolean HasImmediate { get { return ImmediateBit == 1; } }

#region Initialization
        public X86OpcodeBytes(Byte opcodeByte)
        {
            Bytes = opcodeByte;
        }

        public X86OpcodeBytes(UInt16 opcodeByte)
        {
            Bytes = opcodeByte;
        }

#endregion
    }

    public struct X86ModeRegRM
    {
        public Byte ModeRM;

        //Attributions according to values

        public Byte Mode 
        {
            get { return (Byte)((ModeRM & 0xc0) >> 6); } 
        }
        public Byte Reg 
        { 
            get { 
                return (Byte)((ModeRM & 0x38) >> 3); 
            } 
        }
        public Byte RM 
        {
            get { return (Byte)(ModeRM & 0x7); } 
        }


//MOD R/M Addressing Mode
//=== === ================================
// 00 000 [ eax ]
// 01 000 [ eax + disp8 ]               (1)
// 10 000 [ eax + disp32 ]
// 11 000 register  ( al / ax / eax )   (2)
// 00 001 [ ecx ]
// 01 001 [ ecx + disp8 ]
// 10 001 [ ecx + disp32 ]
// 11 001 register  ( cl / cx / ecx )
// 00 010 [ edx ]
// 01 010 [ edx + disp8 ]
// 10 010 [ edx + disp32 ]
// 11 010 register  ( dl / dx / edx )
// 00 011 [ ebx ]
// 01 011 [ ebx + disp8 ]
// 10 011 [ ebx + disp32 ]
// 11 011 register  ( bl / bx / ebx )
// 00 100 SIB  Mode                     (3)
// 01 100 SIB  +  disp8  Mode
// 10 100 SIB  +  disp32  Mode
// 11 100 register  ( ah / sp / esp )
// 00 101 32-bit Displacement-Only Mode (4)
// 01 101 [ ebp + disp8 ]
// 10 101 [ ebp + disp32 ]
// 11 101 register  ( ch / bp / ebp )
// 00 110 [ esi ]
// 01 110 [ esi + disp8 ]
// 10 110 [ esi + disp32 ]
// 11 110 register  ( dh / si / esi )
// 00 111 [ edi ]
// 01 111 [ edi + disp8 ]
// 10 111 [ edi + disp32 ]
// 11 111 register  ( bh / di / edi )



        public Boolean HasDisplacement
        {
            get { return (Mode == 1) || (Mode == 2) || (Mode == 0 && RM == 5); }
        }

        public Boolean HasSIB
        {
            get { return (RM == 4) && (Mode != 4); }
        }

        public Boolean AccessToMemory
        {
            get { return Mode != 3; }
        }

        /// <summary>
        /// 8/16/32 bit register index of direct register access
        /// </summary>
        public byte DirectRegisterIndex
        {
            get
            {
                if (AccessToMemory)
                    return (byte)_32BitRegister.None;
                else
                {
                    switch (RM)
                    {
                        case 0:
                            return (byte)_32BitRegister.EAX;
                        case 1:
                            return (byte)_32BitRegister.ECX;
                        case 2:
                            return (byte)_32BitRegister.EDX;
                        case 3:
                            return (byte)_32BitRegister.EBX;
                        case 4:
                            return (byte)_32BitRegister.ESP;
                        case 5:
                            return (byte)_32BitRegister.EBP;
                        case 6:
                            return (byte)_32BitRegister.ESI; //_8BitRegister.CH
                        case 7:
                            return (byte)_32BitRegister.EDI; //_8BitRegister.DH
                        default:
                            Debug.Assert(false, "Inconsitent situation");
                            return (byte)_32BitRegister.Unknown;
                    }
                }
            }
        }

        /// <summary>
        /// 32 bit register index if addressing by value of register
        /// [EAX]/[ESI + Displacement]
        /// </summary>
        public _32BitRegister BaseRegister
        {
            get
            {
                if (!AccessToMemory)
                    return _32BitRegister.None;
                else
                {
                    switch (RM)
                    {
                        case 0:
                            return _32BitRegister.EAX;
                        case 1:
                            return _32BitRegister.ECX;
                        case 2:
                            return _32BitRegister.EDX;
                        case 3:
                            return _32BitRegister.EBX;
                        //case 4:
                        //    return _32BitRegister.ESP; no [ESP] direct mode
                        case 5:
                            {
                                if (Mode == 0) //there's no [EBP] mode
                                    return _32BitRegister.None;
                                else
                                    return _32BitRegister.EBP;
                            }
                        case 6:
                            return _32BitRegister.ESI; //_8BitRegister.CH
                        case 7:
                            return _32BitRegister.EDI; //_8BitRegister.DH
                        default:
                            Debug.Assert(false, "Inconsitent situation");
                            return _32BitRegister.Unknown;
                    }
                }
            }
        }
    }

    

    public struct X86SIB
    {
        public Byte SIB;
        public Byte Scale { get { return (Byte)((SIB & 0xc0) >> 6); } }
        public Byte Index { get { return (Byte)((SIB & 0x38) >> 3); } }
        public Byte Base { get { return (Byte)(SIB & 0x7); } }

        public _32BitRegister IndexRegister
        {
            get
            {
                switch (Index)
                {
                    case 0:
                        return _32BitRegister.EAX;
                    case 1:
                        return _32BitRegister.ECX;
                    case 2:
                        return _32BitRegister.EDX;
                    case 3:
                        return _32BitRegister.EBX;
                    //case 4:Illegal
                        
                    case 5:
                        return _32BitRegister.EBP;
                    case 6:
                        return _32BitRegister.ESI;
                    case 7:
                        return _32BitRegister.EDI;
                    default:
                        System.Diagnostics.Debug.Assert(false, "Illegal register");
                        return _32BitRegister.Unknown;
                }
            }
        }

      
        //Scaled Indexed Addressing Mode
//[ reg32 + eax*n ] MOD = 00
//[ reg32 + ebx*n ] 
//[ reg32 + ecx*n ]
//[ reg32 + edx*n ]
//[ reg32 + ebp*n ]
//[ reg32 + esi*n ]
//[ reg32 + edi*n ]

//[ disp + reg8 + eax*n ] MOD = 01
//[ disp + reg8 + ebx*n ]
//[ disp + reg8 + ecx*n ]
//[ disp + reg8 + edx*n ]
//[ disp + reg8 + ebp*n ]
//[ disp + reg8 + esi*n ]
//[ disp + reg8 + edi*n ]

//[ disp + reg32 + eax*n ] MOD = 10
//[ disp + reg32 + ebx*n ]
//[ disp + reg32 + ecx*n ]
//[ disp + reg32 + edx*n ]
//[ disp + reg32 + ebp*n ]
//[ disp + reg32 + esi*n ]
//[ disp + reg32 + edi*n ]

//[ disp + eax*n ] MOD = 00, and
//[ disp + ebx*n ] BASE field = 101
//[ disp + ecx*n ]
//[ disp + edx*n ]
//[ disp + ebp*n ]
//[ disp + esi*n ]
//[ disp + edi*n ]
    }

    public struct X86Displacement
    {
        public int Displacement;
        public X86OperandSize Length;

        public SByte DispByte
        {
            get { Debug.Assert(Length == X86OperandSize._8bit); return (SByte)Displacement; }
        }

        public UInt16 DispWord
        {
            get { Debug.Assert(Length == X86OperandSize._16bit); return (UInt16)Displacement; }
        }

        public Boolean IsNagative
        {
            get {
                if (Length == X86OperandSize._8bit)
                    return ((Byte)Displacement & 0x80) != 0;
                else if (Length == X86OperandSize._16bit)
                    return ((UInt16)Displacement & 0x8000) != 0;
                else if (Length == X86OperandSize._32bit)
                    return (Displacement & 0x80000000) != 0;
                else
                {
                    throw new NotImplementedException();
                }
            }
        }

    }

    public struct X86Immediate
    {
        public int Immediate;
        public X86OperandSize Length;
    }

    public class X86AddressingDescriptor
    {
        public override String ToString()
        {
            //Addressing = [Base + Index*Scale + Disp]
            //or register direct
            if (!ModRM.AccessToMemory)
            {
                //only display register
                if (Size == X86OperandSize._8bit)
                    return ((_8BitRegister)Enum.ToObject(typeof(_8BitRegister), ModRM.DirectRegisterIndex)).ToString();
                else if (Size == X86OperandSize._16bit)
                    return ((_16BitRegister)Enum.ToObject(typeof(_16BitRegister), ModRM.DirectRegisterIndex)).ToString();
                else if (Size == X86OperandSize._32bit)
                    return ((_32BitRegister)Enum.ToObject(typeof(_32BitRegister), ModRM.DirectRegisterIndex)).ToString();
                else
                    throw new NotImplementedException();
            }
            else
            {
                StringBuilder sb = new StringBuilder();
                sb.Append('[');
                //values
                {
                    if (Base != _32BitRegister.None)
                        sb.Append(base.ToString());

                    if (ModRM.HasSIB)
                        sb.Append(String.Format("+{0}*{1}", Index, Scale + 1)); //Scale Index must has a base
                    if (ModRM.HasDisplacement)
                    {
                        // use '-' if displacement is nagtive
                        if (sb.Length > 0)
                        {
                            if (Disp.IsNagative)
                            {
                                sb.Append(String.Format("-{0}", Math.Abs(Disp.Displacement)));
                            }
                            else
                            {
                                sb.Append(String.Format("+{0}", Disp.Displacement));
                            }
                        }
                        else // Displacement only mode
                            sb.Append(String.Format("{0:X}", Disp.Displacement));
                    }
                }
                sb.Append(']');
                return sb.ToString();
            }
        }

        public byte Scale
        {
            get
            {
                //Debug.Assert(!ModRM.HasSIB, "Should have no scale value");
                if (!ModRM.HasSIB)
                    return 0;
                return SIB.Scale;
            }
        }

        public _32BitRegister Index
        {
            get
            {
                if (!ModRM.HasSIB)
                    return _32BitRegister.None;
                return SIB.IndexRegister;
            }
        }

        /// <summary>
        /// The Base is not the base of SIB, but BASE register when address like [EAX+ disp], EAX is the base
        /// </summary>
        public _32BitRegister Base
        {
            get
            {
                if (ModRM.HasSIB) //base in SIB
                {
                    switch (SIB.Base)
                    {
                        case 0:
                            return _32BitRegister.EAX;
                        case 1:
                            return _32BitRegister.ECX;
                        case 2:
                            return _32BitRegister.EDX;
                        case 3:
                            return _32BitRegister.EBX;
                        case 4:
                            return _32BitRegister.ESP;
                        case 5:
                            if (ModRM.Mode == 0)
                                return _32BitRegister.None;
                            else if (ModRM.Mode == 1 || ModRM.Mode == 2)
                                return _32BitRegister.EBP;
                            else
                            {
                                Debug.Assert(false, "Inconsistent state");
                                return _32BitRegister.Unknown;
                            }
                        case 6:
                            return _32BitRegister.ESI;
                        case 7:
                            return _32BitRegister.EDI;
                        default:
                            System.Diagnostics.Debug.Assert(false, "Illegal base register");
                            return _32BitRegister.Unknown;
                    }
                }
                else
                    return ModRM.BaseRegister;
            }
        }


        public X86ModeRegRM ModRM;
        public X86SIB SIB;
        public X86Displacement Disp;
        public X86OperandSize Size;
    }



    public class X86Instruction
    {
        public X86PrefixBytes Prefix;
        public X86OpcodeBytes Opcode;

        public X86AddressingDescriptor Addressing;

        //public X86ModeRegRM ModeRM;
        //public X86SIB SIB;
        //public X86Displacement Displacement;
        public X86Immediate Immediate;
        public IMemoryArea Bytes;

        #region attributions

        public Byte Length
        {
            get 
            {
                //prefix + opcode + MODRM +SIB + Displacement + Immediate
                throw new NotImplementedException();
            }
        }

        public X86OperandSize OperandSize
        {
            get
            {
                Boolean bHasSizeOverridePfx = false;
                if (this.Prefix != null)
                {
                    for (int i = 0; i < this.Prefix.Length; i++)
                    {
                        if (this.Prefix[i] == X86InstPrefix.OperandSizeOverride) ;
                        {
                            bHasSizeOverridePfx = true;
                            break;
                        }
                    }
                }
                return GetInstructionOperandSize(bHasSizeOverridePfx, Opcode.SizeBit);
            }
        }

        public static X86OperandSize GetInstructionOperandSize(Boolean HasSizeOverridePfx, Byte sizeBit)
        {
            //To specify a 16-bit operand (under Windows or Linux) you must insert a special operand-size prefix byte in front of the instruction
            if (HasSizeOverridePfx)
                return X86OperandSize._16bit;
            else
                return (sizeBit == 1) ? X86OperandSize._32bit : X86OperandSize._8bit;
        }

        public X86OperandSize ImmediateSize
        {
            get
            {
                Debug.Assert(Opcode.HasImmediate, "Constant size has no meaning when instruction has not immediate value");

                if (OperandSize == X86OperandSize._8bit)
                    return X86OperandSize._8bit;
                else
                {
                    if (Opcode.DirectionSignExtBit == 0)
                        return OperandSize;
                    else
                        return X86OperandSize._8bit;
                }
            }
        }
        
        #endregion

    }

    public interface IInstrutionParser
    {
        int GetInstructionLength(Byte[] binary);
        int GetInstructionLength(Byte[] binary, int offset);
        int GetInstructionLength(IntPtr pNativeMem);
        int GetInstructionLength(IntPtr pNativeMem, int offset);
    }

    public interface IMemoryArea
    {
        int Length { get; }
        Byte this[int index] { get; }
    }

    public enum DisassemblerError
    {
        Success,
        UnknownOpcode,
        InvalidData,
        InsufficentBuffer,
        InvalidParameter,
    }

    public class Disassembler
    {
        private IMemoryArea _Memory;

        private int _currentOffset;

        public DisassemblerError Decode()
        {
            throw new NotFiniteNumberException();
        }

        static Disassembler()
        {
            X86Dism.Initialize();
        }

        public static DisassemblerError BytesToInstruction(Byte[] bytes, int nOffset, out int nCurOffset, out LinkedList<X86Instruction> insList)
        {
            insList = new LinkedList<X86Instruction>();
            nCurOffset = nOffset;

            if (nOffset >= bytes.Length)
                return DisassemblerError.InvalidParameter;

            Boolean bTwoByteOpcode = false;

            
            while (nCurOffset <= bytes.Length)
            {
                //prefix
                //lookup prefix/opcode
                X86PrefixBytes prefix = null;

                X86InstructionInfo info = null;
                if (!bTwoByteOpcode)
                    info = X86Dism.SingleByteInstructionTable[bytes[nCurOffset]];
                else
                    info = X86Dism.DoubleByteInstructionTable[bytes[nCurOffset]];

                if (info.MainGroup == MainInstructionGroup.Prefix)
                {
                    if (bTwoByteOpcode) //prefix cannot exist after two byte flag
                        return DisassemblerError.InvalidData;

                    if (prefix == null)
                        prefix = new X86PrefixBytes(bytes[nCurOffset]);
                    else
                        prefix.Add(bytes[nCurOffset]);

                }
                else if (bytes[nCurOffset] == X86Dism.TwoByteInstructionPrefix)
                {
                    if (bTwoByteOpcode)
                        return DisassemblerError.InvalidData;

                    bTwoByteOpcode = true;
                }
                else //opcode
                {
                    X86Instruction inst = new X86Instruction();
                    inst.Prefix = prefix;
                    inst.Opcode.Bytes = (bTwoByteOpcode) ? (UInt16)(X86Dism.TwoByteInstructionPrefix << 8 + bytes[nCurOffset]) : bytes[nCurOffset];

                    //modrm/SIB/displacement/immediate if any
                    if (info.UseModRM == 1)
                    {

                    }

                    //reset prefix
                    prefix = null;
                    //reset two byte prefix
                    bTwoByteOpcode = false;
                }


                nCurOffset ++;
            }
        }
    }
}
